#include "const/oPreDefine.h"
#include "physics/oBody.h"
#include "physics/oGroup.h"
#include "physics/oSensor.h"

oBody::oBody(oWorld* world, oBodyDef* bodyDef):
_bodyB2(nullptr),
_bodyDef(bodyDef),
_world(world)
{
	_bodyDef->retain();
	_bodyB2 = world->getB2World()->CreateBody(_bodyDef);
	BOOST_FOREACH(b2FixtureDef* fixtureDef, _bodyDef->getFixtures())
	{
		oBody::attach(fixtureDef);
	}
	_bodyB2->SetUserData((void*)this);
}

oBody::~oBody()
{
	if (_bodyB2)
	{
		_world->getB2World()->DestroyBody(_bodyB2);
		_bodyB2 = nullptr;
		for (uint32 i = 0;i < _sensors.count();i++)
		{
			_sensors.getObjectAtIndex(i)->setEnable(false);
		}
		_sensors.removeAllObjects();
	}
	_world = nullptr;
	SAFE_RELEASE_NULL(_bodyDef);
}


void oBody::onEnter()
{
	CCNode::onEnter();
	_bodyB2->SetActive(true);
}

void oBody::updateTransform()
{
	if (_bodyB2->IsAwake())
	{
		const b2Vec2& pos = _bodyB2->GetPosition();
		/* Here only CCNode::setPosition(const CCPoint& var) work for modify CCNode`s position.
		 Other positioning functions have been overridden by oBody`s.
		*/
		CCNode::setPosition(CCPoint(oWorld::oVal(pos.x), oWorld::oVal(pos.y)));
		float angle = _bodyB2->GetAngle();
		CCNode::setRotation(-oR2D(angle));
	}
}

void oBody::onExit()
{
	CCNode::onExit();
	_bodyB2->SetActive(false);//Set active false to trigger sensor`s unit leave event.
}

oBodyDef* oBody::getBodyDef() const
{
	return _bodyDef;
}

oBody* oBody::node(oWorld* world, oBodyDef* bodyDef)
{
	oBody* body = new oBody(world, bodyDef);
	body->autorelease();
	return body;
}

oWorld* oBody::getWorld() const
{
	return _world;
}

b2Body* oBody::getB2Body() const
{
	return _bodyB2;
}

oSensor* oBody::getSensorByTag( uint32 tag )
{
	for (uint32 i = 0;i < _sensors.count();i++)
 	{
		oSensor* sensor = _sensors.getObjectAtIndex(i);
		if (sensor->getTag() == tag)
		{
			return sensor;
		}
	}
	return nullptr;
}

bool oBody::removeSensorByTag( uint32 tag )
{
	oSensor* sensor = oBody::getSensorByTag(tag);
	return oBody::removeSensor(sensor);
}

bool oBody::removeSensor( oSensor* sensor )
{
	if (sensor && sensor->getFixture()->GetBody() == _bodyB2)
	{
		sensor->setEnable(false);
		sensor->bodyEnter = NULL;
		sensor->bodyLeave = NULL;
		_bodyB2->DestroyFixture(sensor->getFixture());
		_sensors.removeObject(sensor);
	}
	return false;
}

void oBody::setVelocity( float x, float y )
{
	_bodyB2->SetLinearVelocity(b2Vec2(oWorld::b2Val(x), oWorld::b2Val(y)));
}

void oBody::setVelocity( const b2Vec2& velocity )
{
	_bodyB2->SetLinearVelocity(oWorld::b2Val(velocity));
}

b2Vec2 oBody::getVelocity() const
{
	return oWorld::oVal(_bodyB2->GetLinearVelocity());
}

void oBody::setGroup( oGroup* group )
{
	_group = group;
	for (b2Fixture* f = _bodyB2->GetFixtureList();f;f = f->GetNext())
	{
		f->SetFilterData(group);
	}
}

oGroup* oBody::getGroup() const
{
	return _group;
}

b2Fixture* oBody::attach( b2FixtureDef* fixtureDef )
{
	fixtureDef->filter = _group;
	b2Fixture* fixture = _bodyB2->CreateFixture(fixtureDef);
	if (fixtureDef->isSensor)
	{
		oSensor* sensor = oSensor::create(this, fixture);
		sensor->setTag((uint32)fixtureDef->userData);
		fixture->SetUserData((void*)sensor);
		_sensors.addObject(sensor);
	}
	return fixture;
}

void oBody::setVelocityX( float x )
{
	_bodyB2->SetLinearVelocityX(oWorld::b2Val(x));
}

float oBody::getVelocityX() const
{
	return oWorld::oVal(_bodyB2->GetLinearVelocityX());
}

void oBody::setVelocityY( float y )
{
	_bodyB2->SetLinearVelocityY(oWorld::b2Val(y));
}

float oBody::getVelocityY() const
{
	return oWorld::oVal(_bodyB2->GetLinearVelocityY());
}

void oBody::setPosition( const CCPoint& var )
{
	if (var != CCNode::getPosition())
	{
		CCNode::setPosition(var);
		_bodyB2->SetTransform(oWorld::b2Val(var), _bodyB2->GetAngle());
	}
}

void oBody::setRotation( float var )
{
	if (var != CCNode::getRotation())
	{
		CCNode::setRotation(var);
		_bodyB2->SetTransform(_bodyB2->GetPosition(), -oD2R(var));
	}
}

void oBody::setPosition( float x, float y )
{
	this->setPosition(CCPoint(x,y));
}

void oBody::destroy()
{
	this->getParent()->removeChild(this, true);
}
